package com.cy.common.config;

import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;

/**
 * @Configuration 注解描述的类为一个配置对象,此对象也会交给spring管理
 */
@Configuration
public class SpringShiroConfig {
    /**
     * Session是什么？此对象时在服务端记录客户端与服务端会话的状态对象
     * 这个对象一般是一个会话创建一个，并且会有一个唯一的标识（JSESSIONID),
     * 可以通过这样的对象来记录登录用户信息，记录购物车信息，记录验证码信息
     *
     * SessionManager是一个管理session的一个对象
     * @return
     */
    @Bean
    public SessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(60*60*1000);//设置超时时间一个小时
        //session对象由服务端创建，但此对象创建好以后会将id以cookie会话存入客户端
        //客户端访问服务器时，会携带id到服务端，然后基于id匹配相对于的Session

       //重写url时，会在url的后边添加一个JsessionId，默认开启
       sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    /**
     * 记住我
     */
    @Bean
    public RememberMeManager rememberMeManager(){
        CookieRememberMeManager rManager = new CookieRememberMeManager();
        SimpleCookie cookie = new SimpleCookie("remeberMe");
        cookie.setMaxAge(7*24*60*60);//设置生存时间
        rManager.setCookie(cookie);
        return rManager;
    }

    /**
     * 配置CacheManager对象，此对象中管理着一个cache对象，此Cache对象可以存储授权时获取的用户
     * 权限信息，下次再授权时，可以直接从缓存中存取用户权限，这样可以减少对数据库的访问压力，并提高其授权权限性能
     * 此Cache对象由SecurityManager调用
     * @return
     */
    @Bean
    public CacheManager shiroCacheManager(){
        return new MemoryConstrainedCacheManager();
    }

    /**
     *  配置授权advisor对象，此对象会在spring启动时加载，并且通过从对象可以找到@RequiresPermissions描述的方法
     *  这些方法在运行时由Advisor对象调用SecurityManager中的checkPermissions方法，
     *  检查用户权限并为访问目标切入点方法做授权操作
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor
    authorizationAttributeSourceAdvisor (SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor= new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * 构建并初始化SecurityManager对象没然后将此对象交给spring管理
     * 说明：@Bean注解应用于@COnfiguration注解描述的类的内部，通过此注解描述的方法
     *  的返回值会交给spring管理，默认bean的名字为方法名
     * @return 返回值shiro中的安全管理器对象，是shiro框架的核心，此对象中实现了
     * 认证，授权，会话缓存，加密等一系列功能的实现，
     */
    @Bean
//    @Autowired 可以直接用参数注入
    public SecurityManager securityManager(Realm realm,CacheManager CacheManager,
                                           RememberMeManager rememberMeManager,SessionManager sessionManager) {
        DefaultWebSecurityManager sManager= new DefaultWebSecurityManager();
        sManager.setRealm(realm);//把realm注入给SecurityManag
        sManager.setCacheManager(CacheManager);//把CacheManager注入给SecurityManager
        sManager.setRememberMeManager(rememberMeManager);
        sManager.setSessionManager(sessionManager);
        return sManager;
    }

    /**
     * 构建并初始化ShiroFilterFactoryBean对象，通过此对象创建过滤器工厂
     * 进而用过过滤器工厂创建过滤器（filter），并通过过滤器对请求信息进行过滤，
     * 例如检测此请求是否需要认证或此请求是否已认证
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactory (SecurityManager securityManager) {
        ShiroFilterFactoryBean sfBean= new ShiroFilterFactoryBean();
        //设置securityManager，基于此对象进行认证检测
        sfBean.setSecurityManager(securityManager);
        sfBean.setLoginUrl("/doLoginUI");
        //定义map指定请求过滤规则(哪些资源允许匿名访问,哪些必须认证访问)
        LinkedHashMap<String,String> map= new LinkedHashMap<>();
        //静态资源允许匿名访问:"anon"
        map.put("/bower_components/**","anon");
        map.put("/build/**","anon");
        map.put("/dist/**","anon");
        map.put("/plugins/**","anon");
        map.put("/user/doLogin","anon");
        map.put("/doLogout","logout");
        //除了匿名访问的资源,其它都要认证("authc")后访问
//        map.put("/**","authc");
        map.put("/**", "user");//可以从用户浏览器cookie中读取数据
        sfBean.setFilterChainDefinitionMap(map);
        return sfBean;
    }
}

